package ar.edu.unlu.sistemas.movirdroid.business.net.tcp;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

import ar.edu.unlu.sistemas.movirdroid.business.msg.MessageInterpreter;
import ar.edu.unlu.sistemas.movirdroid.business.util.Logger;
import ar.edu.unlu.sistemas.ws.business.Message;

public class TCPConnection extends Thread {
	
	private ObjectInputStream input;
	private ObjectOutputStream output;
	private Socket socket;
	
	public TCPConnection (Socket socket){
		try {
			this.setInput(new ObjectInputStream(socket.getInputStream()));
			this.setOutput(new ObjectOutputStream(socket.getOutputStream()));
			this.setSocket(socket);
			this.getSocket().setReceiveBufferSize(1024);
			//this.getSocket().setSoTimeout(4000);
			this.start();
		} catch (IOException ioe) {
			Logger.logError(this.getClass().getName(),"No se pudieron inicializar los Streams",ioe);
			try {
				socket.close();
			} catch (IOException ioe2) {
				Logger.logError(this.getClass().getName(),"Error al cerrar conexion por error de inicializacion Streams",ioe2);
			}
			
		}		
	}

	private ObjectInputStream getInput() {
		return input;
	}

	private void setInput(ObjectInputStream input) {
		this.input = input;
	}

	private ObjectOutputStream getOutput() {
		return output;
	}

	private void setOutput(ObjectOutputStream output) {
		this.output = output;
	}

	public Socket getSocket() {
		return socket;
	}

	private void setSocket(Socket socket) {
		this.socket = socket;
	}

	/**
	 * Finaliza la conexión, cerrando socket y los streams de I/O del mismo. Esto implica que el loop
	 * principal del thread también finalice.
	 */
	public void close() {
		try {
			this.getInput().close();
			this.getSocket().shutdownInput();
			this.getOutput().close();
			this.getSocket().shutdownOutput();
			this.getSocket().close();
		} catch (IOException ioe) {
			Logger.logError(this.getClass().getName(),"Error al cerrar la conexión "+this.getSocket().getRemoteSocketAddress().toString(),ioe);
		}
	}
	
	/**
	 * Envía un mensaje al peer remoto de la conexión. Lo realiza escrbiendo en el stream de salida del socket.
	 * Es probable que otro thread ejecute este método.
	 * 
	 * @param content
	 */
	public void send(Message msg) {
		try {
			Logger.logDebug(this.getClass().getName(),"Envio a "+this.getSocket().getRemoteSocketAddress().toString()+":\n "+msg.getType());
			this.getOutput().writeObject(msg);
		} catch (IOException ioe) {
			Logger.logError(this.getClass().getName(),"No se pudo enviar a"+this.getSocket().getRemoteSocketAddress().toString()+":\n "+msg.getType(),ioe);
		}
	}
	
	/**
	 * Es el loop principal de la conexión. Corre en un thread aparte. Lee el input del socket de la conexión.
	 */
	public void run() {
		Logger.logInfo(this.getClass().getName(),"Inicia el loop de recepcion de mensajes con "+this.getSocket().getRemoteSocketAddress().toString());
		try {
			while (true) {
				Message msg = (Message) this.getInput().readObject();
				Logger.logDebug(this.getClass().getName(), "Recibo de "+this.getSocket().getRemoteSocketAddress().toString()+":\n"+msg.getType());
				MessageInterpreter.interpretMessage(this,msg);
			} 
		} catch (Exception e) {
			Logger.logError(this.getClass().getName(),"Dejo de esperar mensajes para "+this.getSocket().getRemoteSocketAddress().toString(),e);
		}
	}

	
}
